﻿using System;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.CSharp;
using Simhopp.Common;
using System.Windows;
namespace Simhopp.Server
{
    /// <summary>
    /// The Presenter class decreases the coupling between the model and the view of the application.
    /// </summary>
    public class Presenter
    {
        #region FIELD REGION
        readonly Thread _clientHandlerThread;
        readonly ClientHandler _clientHandler;
        #endregion
        #region CONSTRUCTOR REGION
        /// <summary>
        /// Default contructor.
        /// </summary>
        public Presenter()
        {
            var machineName = Environment.MachineName;
            
            MyDbConnection.SetConnectionString((String.Format(@"Data Source={0}\KBMSS;Initial Catalog=Diving;Integrated Security=True;MultipleActiveResultSets=True", machineName)));
            ActiveCompetition.LoadFromDatabase();
            Competition = ReadCompetition.ReadCompetitionById(ActiveCompetition.CompetitionId);
            _clientHandler = new ClientHandler(this);
            ProceedCompetition();
            _clientHandlerThread = new Thread(_clientHandler.RunServer);
            _clientHandlerThread.Start();
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
        }
        #endregion
        #region PROPERTY REGION
        /// <summary>
        /// Gets the client handler manipulated by the presenter.
        /// </summary>
        public ClientHandler ClientHandler
        {
            get { return _clientHandler; }
        } 
        /// <summary>
        /// Gets the competition object manipulated by the presenter.
        /// </summary>
        public Competition Competition { get; private set; }
        #endregion
        #region METHOD REGION
        /// <summary>
        /// Shows a message box with a caught exception.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            MessageBox.Show(String.Format("Uncaught error: \n{0}\n{1}\n{2}", sender.GetType(), e.ExceptionObject, e));
        }
        /// <summary>
        /// Sets the competition object manipulated by the presenter to the specified competition object
        /// and stores it in the database.
        /// </summary>
        /// <param name="competition">The competition to to copy.</param>
        [MethodImpl(MethodImplOptions.Synchronized)]
        public void SetCompetition(Competition competition)
        {
            Competition = competition;
            CompetitionMap.SaveCompetitionToDatabase(Competition);
        }
        /// <summary>
        /// Adds a score to the current dive and proceeds to the next round/diver if necessary.
        /// </summary>
        /// <param name="score">The score to add.</param>
        [MethodImpl(MethodImplOptions.Synchronized)]
        public void SetActiveCompetition(int competitionIndex)
        {
            Competition = ReadCompetition.ReadCompetitionById(competitionIndex);
            competitionIndex = CompetitionMap.SaveCompetitionToDatabase(Competition);
            ActiveCompetition.SaveToDatabase(competitionIndex); 
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void AddScore(Score score)
        {
            var indexOfCurrentRound = Competition.GetIndexOfCurrentRound();
            Competition.GetCurrentDiver().AddScoreToDive(indexOfCurrentRound, score);
            LogConsole.WriteLine("Score added successfully!");

            ProceedCompetition(score);
        }
        /// <summary>
        /// Proceeds to the next round/diver if the requirements are met.
        /// </summary>
        /// <param name="score"></param>
        private void ProceedCompetition(Score score = null)
        {
            if (score != null)
                _clientHandler.SendObjectToAll(new NetworkCommand(Commands.AddVerifiedScore, score));
            if (Competition.CurrentRound != Rounds.Configure)
            {
                var indexOfCurrentRound = Competition.GetIndexOfCurrentRound();
                if (Competition.GetCurrentDiver().NoOfScoresSet(indexOfCurrentRound) == Competition.NumberOfJudges)
                {
                    Competition.GetCurrentDiver().UpdateTotalScore(indexOfCurrentRound);
                    if (Competition.CurrentDiverIndex < Competition.Participants.Count - 1)
                    {
                        _clientHandler.SendObjectToAll(new NetworkCommand(Commands.NextDiver));
                        Competition.NextDiver();
                        LogConsole.WriteLine("Competition proceeding with next diver");
                    }
                    else if (Competition.CurrentRound != Rounds.ThirdRound)
                    {
                        _clientHandler.SendObjectToAll(new NetworkCommand(Commands.NextRound));
                        Competition.NextRound();
                        LogConsole.WriteLine("Competition proceeding with next round");
                    }
                    else
                    {
                        _clientHandler.SendObjectToAll(new NetworkCommand(Commands.Competition, Competition));
                        LogConsole.WriteLine("Competition finished");
                    }
                    CompetitionMap.SaveCompetitionToDatabase(Competition);
                }

            }
        }
        /// <summary>
        /// Kills the processes associated with the server.
        /// </summary>
        public void ExitGraceful()
        {
            _clientHandler.Stop();
            _clientHandlerThread.Join();
        }
        #endregion


    }
}
